home *** CD-ROM | disk | FTP | other *** search
- /*
- * tkUnixEvent.c --
- *
- * This file implements an event source for X displays for the
- * UNIX version of Tk.
- *
- * Copyright (c) 1995-1997 Sun Microsystems, Inc.
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * SCCS: @(#) tkUnixEvent.c 1.16 97/06/06 11:23:15
- */
-
- #include "tkInt.h"
- #include "tkUnixInt.h"
- #include <signal.h>
-
- /*
- * The following static indicates whether this module has been initialized.
- */
-
- static int initialized = 0;
-
- /*
- * Prototypes for procedures that are referenced only in this file:
- */
-
- static void DisplayCheckProc _ANSI_ARGS_((ClientData clientData,
- int flags));
- static void DisplayExitHandler _ANSI_ARGS_((
- ClientData clientData));
- static void DisplayFileProc _ANSI_ARGS_((ClientData clientData,
- int flags));
- static void DisplaySetupProc _ANSI_ARGS_((ClientData clientData,
- int flags));
-
- /*
- *----------------------------------------------------------------------
- *
- * TkCreateXEventSource --
- *
- * This procedure is called during Tk initialization to create
- * the event source for X Window events.
- *
- * Results:
- * None.
- *
- * Side effects:
- * A new event source is created.
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkCreateXEventSource()
- {
- if (!initialized) {
- initialized = 1;
- Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
- Tcl_CreateExitHandler(DisplayExitHandler, NULL);
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * DisplayExitHandler --
- *
- * This function is called during finalization to clean up the
- * display module.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- DisplayExitHandler(clientData)
- ClientData clientData; /* Not used. */
- {
- Tcl_DeleteEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
- initialized = 0;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkpOpenDisplay --
- *
- * Allocates a new TkDisplay, opens the X display, and establishes
- * the file handler for the connection.
- *
- * Results:
- * A pointer to a Tk display structure.
- *
- * Side effects:
- * Opens a display.
- *
- *----------------------------------------------------------------------
- */
-
- TkDisplay *
- TkpOpenDisplay(display_name)
- char *display_name;
- {
- TkDisplay *dispPtr;
- Display *display = XOpenDisplay(display_name);
-
- display = XOpenDisplay(display_name);
-
- if (display == NULL) {
- return NULL;
- }
- dispPtr = (TkDisplay *) ckalloc(sizeof(TkDisplay));
- dispPtr->display = display;
- Tcl_CreateFileHandler(ConnectionNumber(display), TCL_READABLE,
- DisplayFileProc, (ClientData) dispPtr);
- return dispPtr;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkpCloseDisplay --
- *
- * Cancels notifier callbacks and closes a display.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Deallocates the displayPtr.
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkpCloseDisplay(displayPtr)
- TkDisplay *displayPtr;
- {
- TkDisplay *dispPtr = (TkDisplay *) displayPtr;
-
- if (dispPtr->display != 0) {
- Tcl_DeleteFileHandler(ConnectionNumber(dispPtr->display));
-
- (void) XCloseDisplay(dispPtr->display);
- }
-
- ckfree((char *) dispPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * DisplaySetupProc --
- *
- * This procedure implements the setup part of the UNIX X display
- * event source. It is invoked by Tcl_DoOneEvent before entering
- * the notifier to check for events on all displays.
- *
- * Results:
- * None.
- *
- * Side effects:
- * If data is queued on a display inside Xlib, then the maximum
- * block time will be set to 0 to ensure that the notifier returns
- * control to Tcl even if there is no more data on the X connection.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- DisplaySetupProc(clientData, flags)
- ClientData clientData; /* Not used. */
- int flags;
- {
- TkDisplay *dispPtr;
- static Tcl_Time blockTime = { 0, 0 };
-
- if (!(flags & TCL_WINDOW_EVENTS)) {
- return;
- }
-
- for (dispPtr = tkDisplayList; dispPtr != NULL;
- dispPtr = dispPtr->nextPtr) {
-
- /*
- * Flush the display. If data is pending on the X queue, set
- * the block time to zero. This ensures that we won't block
- * in the notifier if there is data in the X queue, but not on
- * the server socket.
- */
-
- XFlush(dispPtr->display);
- if (XQLength(dispPtr->display) > 0) {
- Tcl_SetMaxBlockTime(&blockTime);
- }
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * DisplayCheckProc --
- *
- * This procedure checks for events sitting in the X event
- * queue.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Moves queued events onto the Tcl event queue.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- DisplayCheckProc(clientData, flags)
- ClientData clientData; /* Not used. */
- int flags;
- {
- TkDisplay *dispPtr;
- XEvent event;
- int numFound;
-
- if (!(flags & TCL_WINDOW_EVENTS)) {
- return;
- }
-
- for (dispPtr = tkDisplayList; dispPtr != NULL;
- dispPtr = dispPtr->nextPtr) {
- XFlush(dispPtr->display);
- numFound = XQLength(dispPtr->display);
-
- /*
- * Transfer events from the X event queue to the Tk event queue.
- */
-
- while (numFound > 0) {
- XNextEvent(dispPtr->display, &event);
- Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
- numFound--;
- }
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * DisplayFileProc --
- *
- * This procedure implements the file handler for the X connection.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Makes entries on the Tcl event queue for all the events available
- * from all the displays.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- DisplayFileProc(clientData, flags)
- ClientData clientData; /* The display pointer. */
- int flags; /* Should be TCL_READABLE. */
- {
- TkDisplay *dispPtr = (TkDisplay *) clientData;
- Display *display = dispPtr->display;
- XEvent event;
- int numFound;
-
- XFlush(display);
- numFound = XEventsQueued(display, QueuedAfterReading);
- if (numFound == 0) {
-
- /*
- * Things are very tricky if there aren't any events readable
- * at this point (after all, there was supposedly data
- * available on the connection). A couple of things could
- * have occurred:
- *
- * One possibility is that there were only error events in the
- * input from the server. If this happens, we should return
- * (we don't want to go to sleep in XNextEvent below, since
- * this would block out other sources of input to the
- * process).
- *
- * Another possibility is that our connection to the server
- * has been closed. This will not necessarily be detected in
- * XEventsQueued (!!), so if we just return then there will be
- * an infinite loop. To detect such an error, generate a NoOp
- * protocol request to exercise the connection to the server,
- * then return. However, must disable SIGPIPE while sending
- * the request, or else the process will die from the signal
- * and won't invoke the X error function to print a nice (?!)
- * message.
- */
-
- void (*oldHandler)();
-
- oldHandler = (void (*)()) signal(SIGPIPE, SIG_IGN);
- XNoOp(display);
- XFlush(display);
- (void) signal(SIGPIPE, oldHandler);
- }
-
- /*
- * Transfer events from the X event queue to the Tk event queue.
- */
-
- while (numFound > 0) {
- XNextEvent(display, &event);
- Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
- numFound--;
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkUnixDoOneXEvent --
- *
- * This routine waits for an X event to be processed or for
- * a timeout to occur. The timeout is specified as an absolute
- * time. This routine is called when Tk needs to wait for a
- * particular X event without letting arbitrary events be
- * processed. The caller will typically call Tk_RestrictEvents
- * to set up an event filter before calling this routine. This
- * routine will service at most one event per invocation.
- *
- * Results:
- * Returns 0 if the timeout has expired, otherwise returns 1.
- *
- * Side effects:
- * Can invoke arbitrary Tcl scripts.
- *
- *----------------------------------------------------------------------
- */
-
- int
- TkUnixDoOneXEvent(timePtr)
- Tcl_Time *timePtr; /* Specifies the absolute time when the
- * call should time out. */
- {
- TkDisplay *dispPtr;
- static fd_mask readMask[MASK_SIZE];
- struct timeval blockTime, *timeoutPtr;
- Tcl_Time now;
- int fd, index, bit, numFound, numFdBits = 0;
-
- /*
- * Look for queued events first.
- */
-
- if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
- return 1;
- }
-
- /*
- * Compute the next block time and check to see if we have timed out.
- * Note that HP-UX defines tv_sec to be unsigned so we have to be
- * careful in our arithmetic.
- */
-
- if (timePtr) {
- TclpGetTime(&now);
- blockTime.tv_sec = timePtr->sec;
- blockTime.tv_usec = timePtr->usec - now.usec;
- if (blockTime.tv_usec < 0) {
- now.sec += 1;
- blockTime.tv_usec += 1000000;
- }
- if (blockTime.tv_sec < now.sec) {
- blockTime.tv_sec = 0;
- blockTime.tv_usec = 0;
- } else {
- blockTime.tv_sec -= now.sec;
- }
- timeoutPtr = &blockTime;
- } else {
- timeoutPtr = NULL;
- }
-
- /*
- * Set up the select mask for all of the displays. If a display has
- * data pending, then we want to poll instead of blocking.
- */
-
- memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask));
- for (dispPtr = tkDisplayList; dispPtr != NULL;
- dispPtr = dispPtr->nextPtr) {
- XFlush(dispPtr->display);
- if (XQLength(dispPtr->display) > 0) {
- blockTime.tv_sec = 0;
- blockTime.tv_usec = 0;
- }
- fd = ConnectionNumber(dispPtr->display);
- index = fd/(NBBY*sizeof(fd_mask));
- bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
- readMask[index] |= bit;
- if (numFdBits <= fd) {
- numFdBits = fd+1;
- }
- }
-
- numFound = select(numFdBits, (SELECT_MASK *) &readMask[0], NULL, NULL,
- timeoutPtr);
- if (numFound <= 0) {
- /*
- * Some systems don't clear the masks after an error, so
- * we have to do it here.
- */
-
- memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask));
- }
-
- /*
- * Process any new events on the display connections.
- */
-
- for (dispPtr = tkDisplayList; dispPtr != NULL;
- dispPtr = dispPtr->nextPtr) {
- fd = ConnectionNumber(dispPtr->display);
- index = fd/(NBBY*sizeof(fd_mask));
- bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
- if ((readMask[index] & bit) || (XQLength(dispPtr->display) > 0)) {
- DisplayFileProc((ClientData)dispPtr, TCL_READABLE);
- }
- }
- if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
- return 1;
- }
-
- /*
- * Check to see if we timed out.
- */
-
- if (timePtr) {
- TclpGetTime(&now);
- if ((now.sec > timePtr->sec) || ((now.sec == timePtr->sec)
- && (now.usec > timePtr->usec))) {
- return 0;
- }
- }
-
- /*
- * We had an event but we did not generate a Tcl event from it. Behave
- * as though we dealt with it. (JYL&SS)
- */
-
- return 1;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkpSync --
- *
- * This routine ensures that all pending X requests have been
- * seen by the server, and that any pending X events have been
- * moved onto the Tk event queue.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Places new events on the Tk event queue.
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkpSync(display)
- Display *display; /* Display to sync. */
- {
- int numFound = 0;
- XEvent event;
-
- XSync(display, False);
-
- /*
- * Transfer events from the X event queue to the Tk event queue.
- */
-
- numFound = XQLength(display);
- while (numFound > 0) {
- XNextEvent(display, &event);
- Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
- numFound--;
- }
- }
-